/*
 * This file is part of Herschel Common Science System (HCSS).
 * Copyright 2001-2011 Herschel Science Ground Segment Consortium
 *
 * HCSS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * HCSS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with HCSS.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package ml2;



public class MagnificationMapGenerator {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		//We define the initial size of the shooting region in Einstein radii
		double sizeX = DeflectionContext.margin *DeflectionContext.sizeMap/
		Math.abs(1-DeflectionContext.sigmaMatter - DeflectionContext.shear);
		double sizeY = DeflectionContext.margin *DeflectionContext.sizeMap/
		Math.abs(1-DeflectionContext.sigmaMatter + DeflectionContext.shear);
		double pixelSize = DeflectionContext.sizeMap/DeflectionContext.nPixMap;
		System.out.println("Number of processors:" + DeflectionContext.numberOfProcessors);
		System.out.println("Map size:" + DeflectionContext.sizeMap);
		System.out.println("Size X:" + sizeX);
		System.out.println("Size Y:" + sizeY);
		System.out.println("pixelSize:" + pixelSize);

		//We define by excess the number of pixels in the shooting region and readjusts it sizes
		int nShootX = (int)Math.ceil(sizeX/pixelSize);
		int nShootY = (int)Math.ceil(sizeY/pixelSize);

		sizeX = nShootX*pixelSize;
		sizeY = nShootY*pixelSize;

		System.out.println("Shoot X:" + nShootX);
		System.out.println("Shoot Y:" + nShootY);
		System.out.println("Size X mod:" + sizeX);
		System.out.println("Size Y mod:" + sizeY);
		System.out.println("**************************************************");


		double[][] magnificationMap = Utils.getArray(DeflectionContext.nPixMap, DeflectionContext.nPixMap);
		double[][] shootingMap = Utils.getArray(nShootX, nShootY);

		//Number of stars
		int nStars = (int)Math.round(DeflectionContext.sigmaStars*(sizeX * sizeY));
		System.out.println("Numero de estrellas: " + nStars);
		double xStar[]={sizeX/2};
		double yStar[]={sizeX/2};
		StarBag starBag = new StarBag(nStars, sizeX, sizeY);

		//	StarBag starBag = new StarBag(xStar,yStar);

		Deflector[] deflectors = new  Deflector[DeflectionContext.numberOfProcessors];
		Thread[] deflectorThread = new  Thread[DeflectionContext.numberOfProcessors];
		int nyChunk = nShootY/DeflectionContext.numberOfProcessors;

		for (int i = 0;i<DeflectionContext.numberOfProcessors;i++){
			double startX= -sizeX/2;
			double startY= -sizeY/2 + i*nyChunk*pixelSize;
			deflectors[i] = new PeriodicDeflector(magnificationMap, shootingMap, starBag, 
					startX, nShootX,
					startY, nyChunk,
					sizeX, sizeY,
					pixelSize);
			deflectorThread[i] = new Thread(deflectors[i]);
			double endX = startX + nShootX*pixelSize;
			double endY = startY + nyChunk*pixelSize;
			System.out.println("Processor "+i);
			System.out.println("Start X:"+startX);
			System.out.println("End X:"+endX);
			System.out.println("Start Y:"+startX);
			System.out.println("End Y:"+endY);
		}

		//Start all the deflectors
		for (int i = 0;i<DeflectionContext.numberOfProcessors;i++){
			deflectorThread[i].start();
		}	

		boolean working = true;
		//Wait until all have been finished;
		while (working){
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			working = false;
			for (int i = 0;i<DeflectionContext.numberOfProcessors;i++){ 
				working |= deflectorThread[i].isAlive();
				System.out.println(deflectors[i].toString());
			}	
		}
		//Compute flux parameters
		long totalFlux =0;
		long lostFlux =0;
		double maxTime = Double.MIN_VALUE;
		for (int t = 0; t<DeflectionContext.numberOfProcessors;t++){
			totalFlux += deflectors[t].totalFlux();
			lostFlux += deflectors[t].lostFlux();
			System.out.println("Time processor " + t +":" + deflectors[t].getUsedTime());
			if (maxTime< deflectors[t].getUsedTime()){
				maxTime = deflectors[t].getUsedTime();
			}
		}

		System.out.println("Total flux: " + totalFlux);
		System.out.println("Lost flux: " + lostFlux);
		System.out.println("Lost fraction: " + ((double)lostFlux)/totalFlux);
		System.out.println("Used time(s): " + Utils.toHHmmss(maxTime));
		System.out.println("Rays/second: " + totalFlux/maxTime);
		System.out.println("Rays/second/processor: " + (totalFlux/maxTime)/DeflectionContext.numberOfProcessors);


		//Calculate magnification
		double meanMagnification =0;
		for (int j=0;j<DeflectionContext.nPixMap;j++){
			for (int i=0;i<DeflectionContext.nPixMap;i++){
				magnificationMap[j][i] /=(DeflectionContext.nRays*DeflectionContext.nRays);
				meanMagnification += magnificationMap[j][i];
			}

		}
		meanMagnification/=(DeflectionContext.nPixMap*DeflectionContext.nPixMap);
		System.out.println("Mean magnification: " + meanMagnification);
		//Saving FITS
		String[] keys ={"SIG_STAR", "SIG_DIST", "SHEAR", "SIZE", "MARGIN", "NRAYS", "NSTARS"};
		String[] values ={Double.toString(DeflectionContext.sigmaStars), 
				Double.toString(DeflectionContext.sigmaMatter), 
				Double.toString(DeflectionContext.shear),
				Double.toString(DeflectionContext.sizeMap), 
				Double.toString(DeflectionContext.margin), 
				Integer.toString(DeflectionContext.nRays*DeflectionContext.nRays),
				Integer.toString(starBag.size())};
		String[] comments ={"Star density", 
				"Extended mass density", 
				"Shear parameter",
				"Map size in Einstein radii", 
				"Shoot region margin factor", 
				"Number of rays per pixel",
		"Number of stars in shooting region"};
		Utils.saveFits(magnificationMap,shootingMap,starBag, "magPattern.fits", keys, values, comments);
		//	Utils.saveFits(, "shootingPattern.fits", keys, values, comments);




	}

}
